package org.xq.softcup.mq.remoting.provider;

import lombok.extern.slf4j.Slf4j;
import org.xq.softcup.mq.registry.ServiceRegistry;
import org.xq.softcup.mq.remoting.net.NetEnum;
import org.xq.softcup.mq.remoting.net.Server;
import org.xq.softcup.mq.remoting.net.params.BaseCallback;
import org.xq.softcup.mq.remoting.net.params.XQRpcRequest;
import org.xq.softcup.mq.remoting.net.params.XQRpcResponse;
import org.xq.softcup.mq.serialize.Serializer;
import org.xq.softcup.mq.util.IpUtil;
import org.xq.softcup.mq.util.NetUtil;
import org.xq.softcup.mq.util.ThrowableUtil;
import org.xq.softcup.mq.util.XQRpcException;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class XQRpcProviderFactory {
    private NetEnum netType;
    private Serializer serializer;
    private String ip;
    private int port;
    private String accessToken;
    private Class<? extends ServiceRegistry> serviceRegistryClass;
    private Map<String, String> serviceRegistryParam;
    private Server server;
    private ServiceRegistry serviceRegistry;
    private String serviceAddress;

    /**
     * 初始化配置方法
     * @param netType       网络类型
     * @param serializer    序列化
     * @param ip            ip地址
     * @param port          端口号
     * @param accessToken   访问令牌
     * @param serviceRegistryClass  服务端注册类
     * @param serviceRegistryParam  服务端注册参数
     */
    public void initConfig(NetEnum netType, Serializer serializer, String ip, int port, String accessToken, Class<? extends ServiceRegistry> serviceRegistryClass, Map<String, String> serviceRegistryParam) {
        this.netType = netType;
        this.serializer = serializer;
        this.ip = ip;
        this.port = port;
        this.accessToken = accessToken;
        this.serviceRegistryClass = serviceRegistryClass;
        this.serviceRegistryParam = serviceRegistryParam;
        if (this.netType == null) {
            throw new XQRpcException("缺少netType参数.");
        }
        if (this.serializer == null) {
            throw new XQRpcException("缺少serializer参数.");
        }
        if (this.ip == null) {
            this.ip = IpUtil.getIp();
        }
        if (this.port <= 0) {
            this.port = 7080;
        }
        if (NetUtil.isPortUsed(this.port)) {
            throw new XQRpcException("端口号[" + this.port + "]已被占用.");
        }
        if ((this.serviceRegistryClass != null) && (this.serviceRegistryParam == null)) {
            throw new XQRpcException("没有注册服务参数.");
        }
    }

    public Serializer getSerializer() {
        return this.serializer;
    }

    public int getPort() {
        return this.port;
    }

    public void start() throws Exception {
        this.serviceAddress = IpUtil.getIpPort(this.ip, this.port);
        this.server = ((Server) this.netType.serverClass.newInstance());
        this.server.setStartedCallback(new BaseCallback() {
            public void run() throws Exception {
                if (XQRpcProviderFactory.this.serviceRegistryClass != null) {
                    XQRpcProviderFactory.this.serviceRegistry = ((ServiceRegistry) XQRpcProviderFactory.this.serviceRegistryClass.newInstance());
                    XQRpcProviderFactory.this.serviceRegistry.start(XQRpcProviderFactory.this.serviceRegistryParam);
                    if (XQRpcProviderFactory.this.serviceData.size() > 0) {
                        XQRpcProviderFactory.this.serviceRegistry.registry(XQRpcProviderFactory.this.serviceData.keySet(), XQRpcProviderFactory.this.serviceAddress);
                    }
                }
            }
        });
        this.server.setStopedCallback(new BaseCallback() {
            public void run() {
                if (XQRpcProviderFactory.this.serviceRegistry != null) {
                    if (XQRpcProviderFactory.this.serviceData.size() > 0) {
                        XQRpcProviderFactory.this.serviceRegistry.remove(XQRpcProviderFactory.this.serviceData.keySet(), XQRpcProviderFactory.this.serviceAddress);
                    }
                    XQRpcProviderFactory.this.serviceRegistry.stop();
                    XQRpcProviderFactory.this.serviceRegistry = null;
                }
            }
        });
        this.server.start(this);
    }

    public void stop() throws Exception {
        this.server.stop();
    }

    private Map<String, Object> serviceData = new HashMap();

    public Map<String, Object> getServiceData() {
        return this.serviceData;
    }

    public static String makeServiceKey(String iface, String version) {
        String serviceKey = iface;
        if ((version != null) && (version.trim().length() > 0)) {
            serviceKey = serviceKey + "#".concat(version);
        }
        return serviceKey;
    }

    public void addService(String iface, String version, Object serviceBean) {
        String serviceKey = makeServiceKey(iface, version);
        this.serviceData.put(serviceKey, serviceBean);
        log.info("服务提供添加成功！serviceKey = {}, serviceBean = {}", serviceKey, serviceBean.getClass());
    }

    public XQRpcResponse invokeService(XQRpcRequest xqRpcRequest) {
        XQRpcResponse xqRpcResponse = new XQRpcResponse();
        xqRpcResponse.setRequestId(xqRpcRequest.getRequestId());

        String serviceKey = makeServiceKey(xqRpcRequest.getClassName(), xqRpcRequest.getVersion());
        Object serviceBean = this.serviceData.get(serviceKey);
        if (serviceBean == null) {
            xqRpcResponse.setErrorMsg("服务Key[" + serviceKey + "]未找到.");
            return xqRpcResponse;
        }
        if (System.currentTimeMillis() - xqRpcRequest.getCreateMillisTime() > 180000L) {
            xqRpcResponse.setErrorMsg("响应已超时.");
            return xqRpcResponse;
        }
        if ((this.accessToken != null) && (this.accessToken.trim().length() > 0) && (!this.accessToken.trim().equals(xqRpcRequest.getAccessToken()))) {
            xqRpcResponse.setErrorMsg("访问Token[" + xqRpcRequest.getAccessToken() + "]不正确.");
            return xqRpcResponse;
        }
        try {
            Class<?> serviceClass = serviceBean.getClass();
            String methodName = xqRpcRequest.getMethodName();
            Class<?>[] parameterTypes = xqRpcRequest.getParameterTypes();
            Object[] parameters = xqRpcRequest.getParameters();

            Method method = serviceClass.getMethod(methodName, parameterTypes);
            method.setAccessible(true);
            Object result = method.invoke(serviceBean, parameters);
            xqRpcResponse.setResult(result);
        } catch (Throwable t) {
            log.error("调用服务器错误.", t);
            xqRpcResponse.setErrorMsg(ThrowableUtil.toString(t));
        }
        return xqRpcResponse;
    }
}
